home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 24
/
AACD 24.iso
/
CDTools
/
Eject
/
Source
/
Eject.c
Wrap
C/C++ Source or Header
|
1994-12-25
|
18KB
|
803 lines
/*
** Eject - AmigaDOS 2.04 utility
**
** Copyright © 1994-1994 by Olaf `Olsen' Barthel
** Public Domain
**
** :ts=4
*/
/* Just a few includes... */
#include <intuition/intuitionbase.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/scsidisk.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#include <clib/dos_protos.h>
#include <clib/wb_protos.h>
#include <string.h>
/* SCSI start/stop unit command. */
struct StartStopUnit
{
UBYTE OperationCode, /* 0x1B */
LUN_Reserved_Immed, /* %AAABBBBC, A = LUN, C = Immediate (0 = wait for completion) */
Reserved[2],
LoEj_Start, /* %000000AB, A = Load/Eject (1 = eject after stop), */
Control; /* B = Start/Stop (1 = Start, 0 = Stop) */
};
/* SCSI test unit ready command. */
struct TestUnitReady
{
UBYTE Command, /* 0x00 */
LUN,
Reserved[3],
Control;
};
/* SCSI inquiry command. */
struct Inquiry
{
UBYTE Command, /* 0x12 */
LUN,
PageCode,
Reserved,
AllocationLength,
Control;
};
/* Data format returned by inquiry command. */
struct InquiryData
{
UBYTE PeripheralType,
DeviceTypeModifier,
Version,
Reserved[5],
Vendor[8],
Product[16],
Revision[4];
};
/* Version identification. */
STRPTR Version = "$VER: Eject 1.0 (25.12.94)\r\n";
/* Global library bases. */
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct IntuitionBase *IntuitionBase;
struct Library *IconBase,
*WorkbenchBase;
/* Shell command template data. */
STRPTR Template = "DEVICE/A/M";
enum { ARG_DEVICE=0, ARGCOUNT };
/* Prototypes for this module. */
BYTE DoSCSICmd(struct IOStdReq *DeviceRequest,ULONG Unit,APTR Command,LONG CommandLen,APTR Data,LONG DataLen);
BYTE TestUnitReady(struct IOStdReq *DeviceRequest,ULONG Unit);
BOOL CheckMediaType(struct IOStdReq *DeviceRequest,ULONG Unit);
BYTE EjectMedia(struct IOStdReq *DeviceRequest,ULONG Unit);
BOOL Eject(const STRPTR DeviceName,ULONG Unit,ULONG Flags,BOOL Echo);
BOOL FindDevice(const BPTR FileLock,const STRPTR FileName,STRPTR Device,ULONG *Unit,ULONG *Flags);
/* Main(VOID):
*
* Program entry point.
*/
LONG __saveds
Main(VOID)
{
struct Process *ThisProcess;
LONG Result = RETURN_FAIL;
struct WBStartup *WBenchMsg;
/* Set up SysBase. */
SysBase = *(struct ExecBase **)4;
/* That's me. */
ThisProcess = (struct Process *)FindTask(NULL);
/* Are we a child of Workbench? */
if(ThisProcess -> pr_CLI)
WBenchMsg = NULL;
else
{
/* Wait for the startup message. */
WaitPort(&ThisProcess -> pr_MsgPort);
WBenchMsg = (struct WBStartup *)GetMsg(&ThisProcess -> pr_MsgPort);
}
/* Open dos.library. */
if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37))
{
/* Workbench invocation? */
if(WBenchMsg)
{
struct MsgPort *IconPort;
/* Hold it... */
Forbid();
/* See if Eject is already running, if so tell
* it to quit.
*/
if(IconPort = FindPort("« Eject »"))
Signal(IconPort -> mp_SigTask,SIGBREAKF_CTRL_C);
Permit();
/* Are we the one and only ?*/
if(!IconPort)
{
/* Create the global port. */
if(IconPort = CreateMsgPort())
{
/* Give it a name and a special priority. */
IconPort -> mp_Node . ln_Name = "« Eject »";
IconPort -> mp_Node . ln_Pri = 1;
/* Open Intuition if possible. */
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37);
/* Take care of the remaining libraries. */
if(IconBase = OpenLibrary("icon.library",37))
{
if(WorkbenchBase = OpenLibrary("workbench.library",37))
{
BPTR OldDir = CurrentDir(WBenchMsg -> sm_ArgList -> wa_Lock);
struct DiskObject *Icon = GetDiskObjectNew(WBenchMsg -> sm_ArgList -> wa_Name);
/* We need the icon. */
if(Icon)
{
struct AppIcon *AppIcon;
/* Add the AppIcon. */
if(AppIcon = AddAppIconA(0,0,FilePart(WBenchMsg -> sm_ArgList -> wa_Name),IconPort,NULL,Icon,NULL))
{
struct AppMessage *Message;
ULONG Signals;
BOOL Done = FALSE;
struct Window *ReqWindow = NULL;
struct AppWindow *AppWindow = NULL;
/* Make the global port visible. */
AddPort(IconPort);
/* Keep going... */
do
{
/* Tie in the window signal if necessary. */
if(ReqWindow)
Signals = 1L << ReqWindow -> UserPort -> mp_SigBit;
else
Signals = NULL;
/* Wait for something to happen. */
Signals = Wait(Signals | (1L << IconPort -> mp_SigBit) | SIGBREAKF_CTRL_C);
/* Do we have the notification window open? */
if(ReqWindow)
{
/* Did the signal originate here? */
if(Signals & (1L << ReqWindow -> UserPort -> mp_SigBit))
{
ULONG IDCMP = NULL;
LONG Result;
/* Process the events. */
Result = SysReqHandler(ReqWindow,&IDCMP,FALSE);
/* Shut the window? */
if(Result == 0 || (Result == -2 && !(IDCMP & IDCMP_RAWKEY)))
{
if(AppWindow)
{
RemoveAppWindow(AppWindow);
AppWindow = NULL;
}
FreeSysRequest(ReqWindow);
ReqWindow = NULL;
}
}
}
/* A signal from Workbench? */
if(Signals & (1L << IconPort -> mp_SigBit))
{
/* Pick up all incoming data. */
while(Message = (struct AppMessage *)GetMsg(IconPort))
{
/* User dropped icons on the AppWindow/AppIcon. */
if(Message -> am_NumArgs)
{
UBYTE DeviceName[256];
ULONG Unit,Flags;
LONG i;
/* Take care of all entries. */
for(i = 0 ; i < Message -> am_NumArgs ; i++)
{
if(FindDevice(Message -> am_ArgList[i] . wa_Lock,Message -> am_ArgList[i] . wa_Name,DeviceName,&Unit,&Flags))
Eject(DeviceName,Unit,Flags,FALSE);
}
}
else
{
/* It's just a double-click on the icon.
* See if the notification window is
* already open.
*/
if(ReqWindow)
{
/* Make the window visible. */
if(IntuitionBase -> LibNode . lib_Version >= 39)
ScreenPosition(ReqWindow -> WScreen,SPOS_MAKEVISIBLE,ReqWindow -> LeftEdge,ReqWindow -> TopEdge,ReqWindow -> LeftEdge + ReqWindow -> Width - 1,ReqWindow -> TopEdge + ReqWindow -> Height - 1);
WindowToFront(ReqWindow);
ScreenToFront(ReqWindow -> WScreen);
ActivateWindow(ReqWindow);
}
else
{
/* Did we succeed in opening Intuition? */
if(IntuitionBase)
{
struct EasyStruct Easy;
/* Fill in the data. */
Easy . es_StructSize = sizeof(struct EasyStruct);
Easy . es_Flags = NULL;
Easy . es_Title = (STRPTR)"Eject";
Easy . es_GadgetFormat = (STRPTR)"Continue";
Easy . es_TextFormat = (STRPTR)"\"Eject\" Copyright © 1994-1995 by\nOlaf `Olsen' Barthel <olsen@sourcery.han.de>\nPublic Domain";
/* Open the notification window and just
* for the fun of it make it an AppWindow.
*/
if(ReqWindow = BuildEasyRequest(NULL,&Easy,IDCMP_RAWKEY,NULL))
AppWindow = AddAppWindowA(0,0,ReqWindow,IconPort,NULL);
}
}
}
ReplyMsg((struct Message *)Message);
}
}
/* Terminate? */
if(Signals & SIGBREAKF_CTRL_C)
Done = TRUE;
}
while(!Done);
/* Remove the AppWindow link. */
if(AppWindow)
RemoveAppWindow(AppWindow);
/* Remove the AppIcon. */
RemoveAppIcon(AppIcon);
/* Hide the global port. */
RemPort(IconPort);
/* Dispatch all remaining messages. */
while(Message = (struct AppMessage *)GetMsg(IconPort))
ReplyMsg((struct Message *)Message);
/* Close the notification window if necessary. */
if(ReqWindow)
FreeSysRequest(ReqWindow);
}
/* Get rid of the icon. */
FreeDiskObject(Icon);
}
/* Get back... */
CurrentDir(OldDir);
/* And clean up the libraries. */
CloseLibrary(WorkbenchBase);
}
CloseLibrary(IconBase);
}
if(IntuitionBase)
CloseLibrary((struct Library *)IntuitionBase);
/* Don't forget the global port. */
DeleteMsgPort(IconPort);
}
}
}
else
{
struct RDArgs *ArgsPtr;
STRPTR Args[ARGCOUNT];
/* Clear the args. */
memset(Args,0,sizeof(Args));
/* Read the command arguments if any. */
if(ArgsPtr = ReadArgs(Template,(LONG *)Args,NULL))
{
STRPTR *Names = (STRPTR *)Args[ARG_DEVICE];
/* Process all names. */
while(*Names)
{
UBYTE DeviceName[256];
ULONG Unit,Flags;
BPTR FileLock;
/* Try to get a lock on the object in question. */
FileLock = Lock(*Names,ACCESS_READ);
/* Try to find the corresponding device. */
if(FindDevice(FileLock,*Names,DeviceName,&Unit,&Flags))
{
/* Don't keep the icon longer on the
* desktop than necessary.
*/
UnLock(FileLock);
/* Try to eject the media. */
if(Eject(DeviceName,Unit,Flags,TRUE))
Result = RETURN_OK;
else
{
if(Result == RETURN_FAIL)
Result = RETURN_ERROR;
else
Result = RETURN_WARN;
}
}
else
{
/* Release the file lock. */
UnLock(FileLock);
Printf("Eject: Unable to find device \"%s\".\n",*Names);
if(Result == RETURN_FAIL)
Result = RETURN_ERROR;
else
Result = RETURN_WARN;
}
/* Proceed to the next name... */
Names++;
}
}
else
{
PrintFault(IoErr(),"Eject");
Result = RETURN_ERROR;
}
}
/* Clean up. */
CloseLibrary((struct Library *)DOSBase);
}
else
ThisProcess -> pr_Result2 = ERROR_INVALID_RESIDENT_LIBRARY;
/* Reply the startup message if necessary. */
if(WBenchMsg)
{
Forbid();
ReplyMsg((struct Message *)WBenchMsg);
}
return(Result);
}
/* DoSCSICmd(...):
*
* Send a SCSI command to the driver in question.
*/
BYTE
DoSCSICmd(struct IOStdReq *DeviceRequest,ULONG Unit,APTR Command,LONG CommandLen,APTR Data,LONG DataLen)
{
struct SCSICmd SCSICmd;
UBYTE *CommandArray = Command,
SenseBuffer[20];
/* Patch in the LUN. */
CommandArray[1] |= ((Unit / 10) & 7) << 5;
/* Set up the basic data. */
DeviceRequest -> io_Command = HD_SCSICMD;
DeviceRequest -> io_Data = &SCSICmd;
DeviceRequest -> io_Length = sizeof(struct SCSICmd);
/* Now fill in the command. */
memset(&SCSICmd,0,sizeof(struct SCSICmd));
SCSICmd . scsi_Command = Command;
SCSICmd . scsi_CmdLength = CommandLen;
SCSICmd . scsi_Flags = SCSIF_AUTOSENSE;
SCSICmd . scsi_SenseData = SenseBuffer;
SCSICmd . scsi_SenseLength = 18;
if(Data)
{
SCSICmd . scsi_Data = Data;
SCSICmd . scsi_Length = DataLen;
SCSICmd . scsi_Flags |= SCSIF_READ;
}
else
SCSICmd . scsi_Flags |= SCSIF_WRITE;
/* Cast the dice. */
return(DoIO((struct IORequest *)DeviceRequest));
}
/* TestUnitReady(struct IOStdReq *DeviceRequest,ULONG Unit):
*
* Check if this particular device is ready for action,
* i.e. there is a medium loaded.
*/
BYTE
TestUnitReady(struct IOStdReq *DeviceRequest,ULONG Unit)
{
struct TestUnitReady TestUnitReady;
memset(&TestUnitReady,0,sizeof(TestUnitReady));
return(DoSCSICmd(DeviceRequest,Unit,&TestUnitReady,sizeof(TestUnitReady),NULL,0));
}
/* CheckMediaType(struct IOStdReq *DeviceRequest,ULONG Unit):
*
* Check if the device in question supports removable
* media. However, this does not mean that the device is
* capable of ejecting it on its own.
*/
BOOL
CheckMediaType(struct IOStdReq *DeviceRequest,ULONG Unit)
{
struct Inquiry Inquiry;
struct InquiryData InquiryData;
memset(&Inquiry,0,sizeof(Inquiry));
Inquiry . Command = 0x12;
Inquiry . AllocationLength = sizeof(struct InquiryData);
if(!DoSCSICmd(DeviceRequest,Unit,&Inquiry,sizeof(struct Inquiry),&InquiryData,sizeof(struct InquiryData)))
{
/* A removable medium bit of zero indicates that the
* medium is not removable. A RMB bit of one indicates
* that the medium is removable.
*/
if(InquiryData . DeviceTypeModifier & 0x80)
return(TRUE);
}
return(FALSE);
}
/* EjectMedia(struct IOStdReq *DeviceRequest,ULONG Unit):
*
* Eject the removable medium; this is accomplished via
* the start/stop command. We request that the medium
* should be unloaded after the unit is stopped. This
* command is sometimes referred to as "load unload".
*/
BYTE
EjectMedia(struct IOStdReq *DeviceRequest,ULONG Unit)
{
struct StartStopUnit StartStopUnit;
memset(&StartStopUnit,0,sizeof(StartStopUnit));
StartStopUnit . OperationCode = 0x1B;
StartStopUnit . LoEj_Start = 0x02;
return(DoSCSICmd(DeviceRequest,Unit,&StartStopUnit,sizeof(StartStopUnit),NULL,0));
}
/* Eject(const STRPTR DeviceName,ULONG Unit,ULONG Flags,BOOL Echo):
*
* This routine does the dirty work. Open the device driver,
* check for the device type, see if any media is loaded,
* eject it if necessary.
*/
BOOL
Eject(const STRPTR DeviceName,ULONG Unit,ULONG Flags,BOOL Echo)
{
struct MsgPort *DevicePort;
BOOL Result = FALSE;
/* Set up the interface data... */
if(DevicePort = CreateMsgPort())
{
struct IOStdReq *DeviceRequest;
if(DeviceRequest = (struct IOStdReq *)CreateIORequest(DevicePort,sizeof(struct IOStdReq)))
{
if(!OpenDevice(DeviceName,Unit,(struct IORequest *)DeviceRequest,Flags))
{
Result = TRUE;
/* So far, so good. Now take a look if the device
* supports removable media.
*/
if(CheckMediaType(DeviceRequest,Unit))
{
/* Now check if there is any media loaded.
* We do the check twice in case the medium
* has just been loaded. To indicate this
* case, the SCSI standard requests that the
* device rejects the first command following
* the medium change.
*/
if(!TestUnitReady(DeviceRequest,Unit) && !TestUnitReady(DeviceRequest,Unit))
{
/* Go! */
if(EjectMedia(DeviceRequest,Unit))
{
if(Echo)
Printf("Eject: Error #%ld ejecting media.\n",DeviceRequest -> io_Error);
Result = FALSE;
}
}
}
/* Clean up... */
CloseDevice((struct IORequest *)DeviceRequest);
}
else
{
if(Echo)
Printf("Eject: Error #%ld opening device \"%s\" unit #%ld.\n",DeviceRequest -> io_Error,DeviceName,Unit);
}
DeleteIORequest(DeviceRequest);
}
else
{
if(Echo)
Printf("Eject: Cannot create IOStdReq.\n");
}
DeleteMsgPort(DevicePort);
}
else
{
if(Echo)
Printf("Eject: Cannot create MsgPort.\n");
}
return(Result);
}
/* FindDevice(...):
*
* Find a device by name/filelock.
*/
BOOL
FindDevice(const BPTR FileLock,const STRPTR FileName,STRPTR Device,ULONG *Unit,ULONG *Flags)
{
struct DosList *Entry;
/* For a start, lock the doslist entries we are interested in. */
if(Entry = LockDosList(LDF_DEVICES | LDF_READ))
{
/* Do we have a filelock? */
if(FileLock)
{
struct FileLock *LockPtr = (struct FileLock *)BADDR(FileLock);
/* Walk down the list. */
while(Entry = NextDosEntry(Entry,LDF_DEVICES | LDF_READ))
{
/* If the filing system task that manages this
* device equals the one the filelock originated
* from chances are very high that this is the
* one we were looking for. Please note that this
* technique is not absolutely bullet-proof as in
* theory it is possible to write a filing system
* which manages multiple devices. The task address
* might thus not necessarily point to the right
* doslist entry.
*/
if(Entry -> dol_Task == LockPtr -> fl_Task)
{
struct FileSysStartupMsg *Startup = (struct FileSysStartupMsg *)BADDR(Entry -> dol_misc . dol_handler . dol_Startup);
/* Is this a valid entry? There might not be
* a pointer to the FSSM stored here; the
* Port-Handler for example uses this field
* to tell references to SER:, PAR: and PRT:
* apart.
*/
if(TypeOfMem(Startup))
{
/* Is this a valid device name? */
if(TypeOfMem(BADDR(Startup -> fssm_Device)))
{
STRPTR Name = BADDR(Startup -> fssm_Device);
/* Fill in the necessary data. */
strcpy(Device,Name + 1);
*Unit = Startup -> fssm_Unit;
*Flags = Startup -> fssm_Flags;
/* Release the list. */
UnLockDosList(LDF_DEVICES | LDF_READ);
/* Success. */
return(TRUE);
}
}
}
}
}
else
{
UBYTE LocalName[256];
LONG Len;
/* Make a copy of the file name. */
strcpy(LocalName,FileName);
Len = strlen(LocalName);
/* Remove the colon; we expect plain device
* names such as DF0, CD0 or HD0 here.
*/
if(LocalName[Len - 1] == ':')
LocalName[Len - 1] = 0;
/* Try to find the device entry in question. */
if(Entry = FindDosEntry(Entry,LocalName,LDF_DEVICES | LDF_READ))
{
struct FileSysStartupMsg *Startup = (struct FileSysStartupMsg *)BADDR(Entry -> dol_misc . dol_handler . dol_Startup);
if(TypeOfMem(Startup))
{
if(TypeOfMem(BADDR(Startup -> fssm_Device)))
{
STRPTR Name = BADDR(Startup -> fssm_Device);
strcpy(Device,Name + 1);
*Unit = Startup -> fssm_Unit;
*Flags = Startup -> fssm_Flags;
UnLockDosList(LDF_DEVICES | LDF_READ);
return(TRUE);
}
}
}
}
UnLockDosList(LDF_DEVICES | LDF_READ);
}
return(FALSE);
}